﻿using FR.CodeVisualizer.Controls.TreeList;
using FR.CodeVisualizer.Helpers;
using FR.CodeVisualizer.ObjectSource;
using OfficeOpenXml;
using OfficeOpenXml.Table;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FR.CodeVisualizer.Views
{
    public class CollectionViewTree : TreeListView
    {
        public CollectionViewTree()
        {
            ContextMenuStrip = new ContextMenuStrip();
        }

        private SaveFileDialog saveFileDialog;

        List<Entity> entities;

        public enum ColumnType
        {
            Name,
            Value,
            Type
        }

        public int EntitiesCount
        {
            get { return this.Nodes.Count; }
        }

        public void InitCollection(List<Entity> data)
        {
            this.entities = data;
            CreateTree(this.entities);
        }

        public void FilterName(string text, ColumnType column)
        {
            if (string.IsNullOrEmpty(text))
                CreateTree(this.entities);
            else
            {
                BackgroundWorker bw = new BackgroundWorker();
                bw.DoWork += bw_DoWork;
                bw.RunWorkerCompleted += bw_RunWorkerCompleted;
                bw.RunWorkerAsync(new object[] { text, column });
            }
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            List<Entity> filteredEntities = e.Result as List<Entity>;

            CreateTree(filteredEntities);
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            object[] args = e.Argument as object[];

            List<Entity> filteredEntities = new List<Entity>();
            SearchInTree(this.entities, args[0].ToString(), (ColumnType)args[1], null, ref filteredEntities);

            e.Result = filteredEntities;
        }

        private void SearchInTree(List<Entity> entityList, string text, ColumnType column, Entity parent, ref List<Entity> filteredEntities)
        {
            foreach (Entity e in entityList)
            {
                Entity newEntity = new Entity();
                newEntity.Properties = new List<EntityProperties>();
                newEntity.Name = e.Name;

                foreach (EntityProperties entity in e.Properties
                    .Where(c => column == ColumnType.Name && !string.IsNullOrEmpty(c.PropertyName) ?
                        c.PropertyName.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 : column == ColumnType.Value && c.Value != null ?
                        c.Value.ToString().IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 : !string.IsNullOrEmpty(c.PropertyType) ?
                        c.PropertyType.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 : false))
                {
                    EntityProperties newProperty = new EntityProperties
                    {
                        PropertyName = entity.PropertyName,
                        Value = entity.Value,
                        PropertyType = entity.PropertyType
                    };

                    newEntity.Properties.Add(newProperty);
                }

                if (parent != null)
                {
                    //adicionar ao pai respectivo
                    parent.NestedEntities.Add(newEntity);

                    //só adicionar uma vez
                    if (filteredEntities.Select(c => c.Pk == parent.Pk).Count() == 0)
                        filteredEntities.Add(parent);
                }
                else if (newEntity.Properties.Count > 0)
                    filteredEntities.Add(newEntity);

                if (e.NestedEntities != null && e.NestedEntities.Count > 0)
                    SearchInTree(e.NestedEntities, text, column, newEntity, ref filteredEntities);
            }
        }

        public void ExportToExcel()
        {
            if (saveFileDialog == null) saveFileDialog = new SaveFileDialog();

            saveFileDialog.FileName = "";
            saveFileDialog.AddExtension = true;
            saveFileDialog.Filter = "Excel files |*.xlsx";
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                ExcelPackage pck = new ExcelPackage();

                var wsEnum = pck.Workbook.Worksheets.Add("Collection");

                row = 1;
                col = 1;
                PopulateExcelWorksheet(wsEnum, this.entities, 0);

                wsEnum.OutLineSummaryBelow = false;
                wsEnum.Cells[wsEnum.Dimension.Address].AutoFitColumns();

                //...and save
                var fi = new FileInfo(saveFileDialog.FileName);

                pck.SaveAs(fi);

                try
                {
                    Process.Start(fi.FullName);
                }
                catch { }
            }
        }
        int row = 0, col = 0;
        private void PopulateExcelWorksheet(ExcelWorksheet ws, List<Entity> entitiesList, int outline)
        {
            foreach (Entity entity in entitiesList)
            {
                ws.Row(row).OutlineLevel = outline;
                ws.Cells[row, col].Value = entity.Name;

                foreach (EntityProperties property in entity.Properties)
                {
                    row++;

                    ws.Row(row).OutlineLevel = outline + 1;

                    ws.Cells[row, col + 1].Value = property.PropertyName;
                    ws.Cells[row, col + 2].Value = property.Value;
                    ws.Cells[row, col + 3].Value = property.PropertyType;
                }

                if (entity.NestedEntities != null && entity.NestedEntities.Count > 0)
                    PopulateExcelWorksheet(ws, entity.NestedEntities, outline + 1);
            }
        }

        public void ExportToXml()
        {
            if (saveFileDialog == null) saveFileDialog = new SaveFileDialog();

            saveFileDialog.FileName = "";
            saveFileDialog.AddExtension = true;
            saveFileDialog.Filter = "Xml files |*.xml";
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
                XmlSerializer.Serializa(this.entities, saveFileDialog.FileName);
        }

        private void CreateTree(List<Entity> data)
        {
            this.BeginUpdate();

            if (this.Nodes.Count > 0)
                this.Nodes.Clear();

            FillTree(data, null);

            this.EndUpdate();
        }

        private void FillTree(List<Entity> data, Node parentNode)
        {
            foreach (Entity entity in data)
            {
                Node objectNode = new Node();
                objectNode[0] = entity.Name;

                if (entity.Properties != null)
                    foreach (EntityProperties properties in entity.Properties)
                    {
                        Node propertiesNode = new Node();
                        propertiesNode[0] = properties.PropertyName;
                        propertiesNode[1] = properties.Value;
                        propertiesNode[2] = properties.PropertyType;

                        try
                        {
                            propertiesNode.Expanded = true;
                            objectNode.Expanded = true;
                            objectNode.HasChildren = true;
                            objectNode.Nodes.Add(propertiesNode);
                        }
                        catch { }
                    }

                if (entity.NestedEntities != null && entity.NestedEntities.Count > 0)
                    FillTree(entity.NestedEntities, objectNode);

                if (parentNode != null)
                    parentNode.Nodes.Add(objectNode);
                else
                    Nodes.Add(objectNode);
            }
        }

        private void InitializeComponent()
        {
            this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
            ((System.ComponentModel.ISupportInitialize)(this)).BeginInit();
            this.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this)).EndInit();
            this.ResumeLayout(false);
        }
    }
}
